package com.github.linkoog.model_designer.common;

import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import com.intellij.util.xml.ElementPresentationManager;
import com.intellij.util.xml.NamedEnumUtil;

import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class JComboEnumTableCellEditor extends JComboBoxTableCellEditor {

    private final boolean myNullable;
    private final Factory<? extends List<Pair<String, Icon>>> myDataFactory;
    private Map<String, Icon> myData;
    private static final Pair<String,Icon> EMPTY = Pair.create(" ", null);

    public JComboEnumTableCellEditor(Factory<? extends List<Pair<String, Icon>>> dataFactory, final boolean nullable) {
        myDataFactory = dataFactory;
        myNullable = nullable;
        setClickCountToStart(2);
        JComboBox comboBox = (JComboBox)editorComponent;
        comboBox.setBorder(null);
        comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
        initComboBox(comboBox, object -> myData != null && myData.containsKey(object) || myNullable && EMPTY.first == object);
    }

    public JComboEnumTableCellEditor(Class<? extends Enum> anEnum, final boolean nullable) {
        this(createEnumFactory(anEnum), nullable);
    }

    private static JComboBox initComboBox(final JComboBox comboBox, final Condition<? super String> validity) {
        comboBox.setEditable(false);
        comboBox.setPrototypeDisplayValue(new ComboBoxItem("A", null));
        comboBox.setRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                final Pair<String, Icon> pair = (Pair<String, Icon>)value;
                final String text = Pair.getFirst(pair);
                setText(text);
                final Dimension dimension = getPreferredSize();
                if (!validity.value(text)) {
                    setFont(getFont().deriveFont(Font.ITALIC));
                    setForeground(JBColor.RED);
                }
                setIcon(Pair.getSecond(pair));
                setPreferredSize(new Dimension(-1, dimension.height));
                return this;
            }
        });
        return comboBox;
    }

    private static Factory<List<Pair<String, Icon>>> createEnumFactory(final Class<? extends Enum> aClass) {
        return () ->
            Arrays.stream(aClass.getEnumConstants())
                    .map(s ->  Pair.create(NamedEnumUtil.getEnumValueByElement(s), ElementPresentationManager.getIcon(s))).collect(Collectors.toList());
    }

    private static class ComboBoxItem extends Pair<String,Icon> {

        ComboBoxItem(String first, Icon second) {
            super(first, second);
        }

        ComboBoxItem(Pair<String,Icon> pair) {
            super(pair.first, pair.second);
        }

        public String toString() {
            return StringUtil.notNullize(first);
        }
    }

    @Override
    public Object getCellEditorValue() {
        final Pair<String,Icon> cellEditorValue = (Pair<String,Icon>)super.getCellEditorValue();
        return EMPTY == cellEditorValue || null == cellEditorValue ? null : cellEditorValue.first;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        final List<Pair<String, Icon>> list = myDataFactory.create();
        myData = new HashMap<>();

        final JComboBox comboBox = (JComboBox)editorComponent;
        comboBox.removeAllItems();
        if (myNullable) {
            comboBox.addItem(EMPTY);
        }
        for (final Pair<String, Icon> pair : list) {
            myData.put(pair.first, pair.second);
            comboBox.addItem(pair);
        }
        final Pair<Object, Icon> pair = Pair.create(value, myData.get(value));
        comboBox.setEditable(true);
        super.getTableCellEditorComponent(table, pair, isSelected, row, column);
        comboBox.setEditable(false);
        return comboBox;
    }
}
